<?php
declare(strict_types=1);

namespace addons\BuilderPlus\driver;

use addons\BuilderPlus\components\Button;
use addons\BuilderPlus\components\Column;
use addons\BuilderPlus\components\Edit;
use addons\BuilderPlus\components\Search;
use addons\BuilderPlus\contract\TableOptions;
use addons\BuilderPlus\exception\BuilderException;
use addons\BuilderPlus\facade\View;
use think\facade\Cache;
use think\facade\Event;

class Table
{
    public const CONFIG = [
        'button'         => [
            'top'   => ['class' => Button::class, 'container' => 'topButtons'],
            'right' => ['class' => Button::class, 'container' => 'rightButtons'],
        ],
        'event'          => [
            'logs' => \addons\BuilderPlus\contract\event\Logs::class,
        ],
        'defaultToolBar' => 'builderToolBar',
    ];
    private array $columns = [];
    private array $topButtons = [];
    private array $rightButtons = [];
    private array $export = [];
    private array $sortType = ['id' => 'desc'];
    private array $searchItems = [];
    private array $searchTabItems = [];
    private array $options = [];
    private array $specifyQuery = [];
    private array $specifyWithQuery = [];
    private array $tpl = [];
    private string $builderToolbar = '';
    private bool $checkbox = true;
    private bool $totalRow = false;
    private bool $tree = false;
    private array $config = [
        'height'    => '',
        'className' => '',
        'lineStyle' => '',
        'skin'      => 'grid',
        'size'      => 'sm',
        'even'      => false
    ];
    private array $treeConfig = [
        'treeExpandBtn' => false,
        'view'          => [
            'expandAllDefault' => false,
            'iconClose'        => '',
            'iconOpen'         => '',
            'iconLeaf'         => '',
        ],
        'customName'    => [
            'pid'  => 'pid',
            'name' => 'title'
        ],

    ];

    private int $rightButtonsWidth = 130;

    public function init(TableOptions $provider): self
    {
        $this->options = $provider->getOptions();
        return $this;
    }

    public function addTopButtons(array $buttons): self
    {
        return $this->addButtons('top', $buttons);
    }

    public function addRightButtons(array $buttons, int $width = 150, string $toolbar = ''): self
    {
        $this->rightButtonsWidth = $width;
        $this->builderToolbar = $toolbar ?: self::CONFIG['defaultToolBar'];
        return $this->addButtons('right', $buttons);
    }

    public function addButtons(string $type, array $buttons): self
    {
        if (!isset(self::CONFIG['button'][$type])) {
            throw new BuilderException("Invalid button type: {$type}");
        }

        $config = self::CONFIG['button'][$type];
        $container = $config['container'];

        foreach ($buttons as $key => $definition) {
            if (empty($definition)) continue;
            $name = is_string($key) ? $key : $definition;
            $button = $this->createButton($name, $definition);
            $this->{$container}[$name] = $button;
        }

        return $this;
    }

    private function createButton(string $name, $definition): Button
    {
        $button = new Button($name);
        if ($definition instanceof \Closure) {
            $definition->call($button, $button);
        }
        return $button;
    }

    public function addColumns(array $columns): self
    {
        foreach ($columns as $column) {
            if (empty($column)) continue;
            [$field, $title, $config, $editConfig] = array_pad($column, 4, null);
            $this->addColumn($field, $title, $config, $editConfig);
        }
        return $this;
    }

    public function addColumn(string $field, string $title, \Closure|string|null $config = '', \Closure|string|null $editConfig = ''): self
    {
        if (isset($this->columns[$field])) {
            return $this;
        }

        $column = new Column($field, $title);

        if ($config instanceof \Closure) {
            $config->call($column, $column);
        }

        if ($editConfig) {
            $edit = new Edit();
            if ($editConfig instanceof \Closure) {
                $editConfig->call($edit, $edit);
            }
            $column->editCol($edit->render());
        }


        $this->columns[] = $column;


        return $this;
    }

    public function addSearch(array $searchList): self
    {
        foreach ($searchList as $key => $search) {
            if (empty($search)) continue;
            $searchItem = $this->createSearch($search[0], $search[1], $search[2] ?? '');
            $this->searchItems[] = $searchItem;
        }
        return $this;
    }

    public function addSearchTab(array $searchList): self
    {
        foreach ($searchList as $key => $search) {
            if (empty($search)) continue;
            $searchItem = $this->createSearch($search[0], $search[1], $search[2] ?? '');
            $this->searchTabItems[] = $searchItem;
        }
        return $this;
    }

    public function addTpl(array $tpl): self
    {
        $this->tpl = $tpl;
        return $this;
    }

    private function createSearch(string $field, string $title, $definition): Search
    {
        $search = new Search($field, $title);

        if ($definition instanceof \Closure) {
            $definition->call($search, $search);
        }

        return $search;
    }

    public function setCheckbox(bool $checkbox): self
    {
        $this->checkbox = $checkbox;
        return $this;
    }

    public function setTotalRow(bool $totalRow): self
    {
        $this->totalRow = $totalRow;
        return $this;
    }

    public function setSortType(array $sortType): self
    {
        $this->sortType = $sortType;
        return $this;
    }

    public function setConfig(array $config): self
    {
        $this->config = [...$this->config, ...$config];
        return $this;
    }


    public function specifyQuery(array $query): self
    {
        $this->specifyQuery = $query;
        return $this;
    }

    public function specifyWithQuery(array $query): self
    {
        $this->specifyWithQuery = $query;
        return $this;
    }

    public function enableTree(bool $default = false, bool $expandBtn = false): self
    {
        $this->tree = true;
        $this->treeConfig['view']['expandAllDefault'] = $default;
        $this->treeConfig['treeExpandBtn'] = $expandBtn;
        return $this;
    }

    public function enableExport(string $type = 'csv', string $title = '', string $url = ''): self
    {
        $this->export = [
            'type'  => $type ?: 'xls',
            'title' => $title ?: request()->nav['s_nav'],
            'url'   => $url
        ];
        return $this;
    }

    public function setTreeConfig(array $config): self
    {
        $this->config = [...$this->treeConfig, ...$config];
        return $this;
    }

    private function setCache(array $components): void
    {
        $components = array_merge($components, [
            'sortType'         => $this->sortType,
            'specifyQuery'     => $this->specifyQuery,
            'specifyWithQuery' => $this->specifyWithQuery,
        ]);
        Cache::set($components['token'], $components);
    }

    public function getCache(string $token): array
    {
        return Cache::get($token);
    }

    public function getConfig(string $key)
    {
        return self::CONFIG[$key];
    }

    public function trigger(string $event, array|object $param): void
    {
        Event::trigger((new (self::CONFIG['event'][$event])($param)));
    }

    private function getToken(): string
    {
        return md5(app('http')->getName() . request()->url());
    }

    public function fetch(): \addons\BuilderPlus\driver\View|string
    {

        return View::fetchIndex($this->getComponents());
    }

    private function getComponents(): array
    {

        if ($this->checkbox) {
            array_unshift($this->columns, (new Column('', '全选'))->type('checkbox')->fixed('left'));
        }

        $url = $this->options['url'] ?? url('getList')->build();

        if (!empty($this->export)) {
            $this->topButtons['tableExport'] = (new Button('tableExport'))
                ->__call('exportType', $this->export['type'])
                ->__call('exportTitle', $this->export['title'])
                ->__call('exportUrl', $this->export['url'])
                ->__call('url', $url . (strpos($url, '?') ? '&' : '?') . 'tableExport=1');
        }

        if (!empty($this->treeConfig['treeExpandBtn'])) {
            $this->topButtons['expandAll'] = (new Button('expandAll'));
        }

        if (!empty($this->rightButtons)) {
            $this->columns[] = (new Column('', '操作'))
                ->fixed('right')
                ->width($this->rightButtonsWidth)
                ->toolbar('#' . $this->builderToolbar);
        }

        $columns = array_map(fn(Column $c) => $c->render(), $this->columns);
        $fastSort = [];
        $friendly = [];
        $fastRatio = [];
        $switch = [];
        $withImgTpl = [];
        $withVideoTpl = [];

        foreach ($columns as $item) {
            if (!empty($item['fastSort'])) $fastSort[$item['field']] = $item;
            if (!empty($item['fastRatio'])) $fastRatio[$item['field']] = $item;
            if (!empty($item['friendly'])) $friendly[$item['field']] = $item;
            if (!empty($item['switch'])) $switch[$item['field']] = $item;
            if (!empty($item['withImgTpl'])) $withImgTpl[] = $item['withImgTpl'];
            if (!empty($item['withVideoTpl'])) $withVideoTpl[] = $item['withVideoTpl'];
        }

        $components = [
            'toolbar'          => $this->builderToolbar,
            'top_buttons'      => array_map(fn(Button $b) => $b->render(), $this->topButtons),
            'right_buttons'    => array_map(fn(Button $b) => $b->render(), $this->rightButtons),
            'search_items'     => array_map(fn(Search $s) => $s->render(), $this->searchItems),
            'search_tab_items' => array_map(fn(Search $s) => $s->render(), $this->searchTabItems),
            'withImgTpl'       => $withImgTpl,
            'withVideoTpl'     => $withVideoTpl,
            'columns'          => $columns,
            'fastSort'         => $fastSort,
            'fastRatio'        => $fastRatio,
            'friendly'         => $friendly,
            'switch'           => $switch,
            'totalRow'         => $this->totalRow,
            'tree'             => $this->tree,
            'config'           => $this->config,
            'treeConfig'       => $this->treeConfig,
            'url'              => $url,
            'tpl'              => $this->tpl,
            'token'            => $this->getToken(),
            'builderPk'        => $this->options['pk'],
            'menu'             => request()->nav ?: []
        ];

        $this->setCache($components);

        return $components;
    }
}